import requests
from .fetcher import Fetcher
from core.queue import MaxQueuePool
from typing import Deque
from log.logger import Logger
from concurrent.futures import ThreadPoolExecutor
from threading import Lock

class PackageDonwloader(object):
    __wlock = Lock()
    __flock = Lock()
    __fetch_pool = None
    __work_pool = None
    __worker_nums = 8
    __fetcher_nums = 3

    @classmethod
    def get_fetch_pool(cls):
        if not cls.__fetch_pool:
            with cls.__flock:
                if not cls.__fetch_pool:
                    cls.__fetch_pool = MaxQueuePool(ThreadPoolExecutor, cls.__fetcher_nums)
        return cls.__fetch_pool

    @classmethod
    def get_work_pool(cls):
        if not cls.__work_pool:
            with cls.__wlock:
                if not cls.__work_pool:
                    cls.__work_pool = MaxQueuePool(ThreadPoolExecutor, cls.__worker_nums)
        return cls.__work_pool

    def __init__(self,
                 pkg_type : str,
                 log_dir: str):
        self.fetcher = Fetcher(pkg_type)
        self.client = requests.Session()
        self.logger = Logger(pkg_type, log_dir)
        self.fetch_pool = PackageDonwloader.get_fetch_pool()
        self.work_pool = PackageDonwloader.get_work_pool()

    def add_task(self, pkg_info):
        self.fetch_pool.submit(self.__add_task, pkg_info)

    def __add_task(self, pkg_info):
        for i in self.fetcher.get_package_releases(pkg_info):
            self.work_pool.submit(self.try_download, i)

    def try_download(self, target_url):
        try:
            req = self.client.head(target_url)
            if req.status_code == 200:
                print("existed: " + target_url)
            else:
                print("try to download: " + target_url)
                req = self.client.get(target_url)
                if req.status_code == 200:
                    print("successfully downloaded: " + target_url)
                    self.logger.success(f"downloaded {target_url}")
                elif req.status_code == 404:
                    self.logger.error(f"[Not Found]: {target_url}")
                else:
                    self.logger.error(f"[{req.status_code}]: {target_url}")
        except requests.exceptions.Timeout as e:
            self.logger.error(f"[Timeout]: {target_url} error: {e}")
        except requests.exceptions.TooManyRedirects:
            self.logger.error(f"[TooManyRedirects]: {target_url} error: {e}")
        except requests.exceptions.RequestException as e:
            self.logger.error(f"[RequestException]: {target_url} error: {e}")
        except Exception as e:
            self.logger.error(f"[UnknownError]: {target_url} error: {e}")

    def wait(self):
        self.__fetch_pool.shutdown(wait=True)
        self.__work_pool.shutdown(wait=True)
